package com.cloud.cli.tk.util;

import com.cloud.cli.model.entity.ColumnEntity;
import com.cloud.cli.model.entity.TableEntity;
import com.cloud.cli.util.GeneratorUtil;
import com.cloud.cli.util.StringUtil;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.cloud.cli.util.GeneratorUtil.columnToJava;

/**
 * 代码生成器   工具类
 *
 * @author chenshun
 * @email sunlightcs@gmail.com
 * @date 2016年12月19日 下午11:40:24
 */
public class TkCliGeUtil implements Serializable {

    public static Map<String, String> getTemplates() {
        Map<String, String> templateMap = new HashMap<>(20);
        // 后端模板
        templateMap.put("osMapperXml", "template/mapper.xml.vm");
        templateMap.put("osPoJava", "template/po.java.vm");
        templateMap.put("osQueryParamJava", "template/query.param.java.vm");
        templateMap.put("osCurdParamJava", "template/curd.param.java.vm");
        templateMap.put("osMapperJava", "template/mapper.java.vm");
        templateMap.put("osCtrlJava", "template/controller.java.vm");
        templateMap.put("osServiceJava", "template/service.java.vm");
        templateMap.put("osServiceImplJava", "template/serviceImpl.java.vm");
        // 前端模板
        templateMap.put("frontRouterJs", "template/vue/router/router.js.vm");
        templateMap.put("frontApiJs", "template/vue/service/api.js.vm");
        templateMap.put("frontCreateVue", "template/vue/view/create.vue.vm");
        templateMap.put("frontDetailVue", "template/vue/view/detail.vue.vm");
        templateMap.put("frontPageVue", "template/vue/view/page.vue.vm");
        templateMap.put("frontUpdateVue", "template/vue/view/update.vue.vm");
        templateMap.put("frontHistoryVue", "template/vue/view/history.vue.vm");
        templateMap.put("frontEnumsJs", "template/vue/enums/enums.js.vm");

        return templateMap;

    }

    /**
     * 生成代码
     */
    public static void generatorCode(Map<String, String> table,
                                     List<Map<String, String>> columns, ZipOutputStream zip) {
        //配置信息
        Configuration config = GeneratorUtil.getConfig();
        //获取表数据新
        TableEntity tableEntity = GeneratorUtil.getTableEntity(table, config, columns);
        //列信息
        List<ColumnEntity> columsList = warpColumnEntityData(columns, tableEntity, config);
        tableEntity.setColumns(columsList);

        //没主键，则第一个字段为主键
        if (tableEntity.getPk() == null) {
            tableEntity.setPk(tableEntity.getColumns().get(0));
        }
        //设置velocity资源加载器
        VelocityContext context = GeneratorUtil.setVelocityContextInfo(tableEntity, config);
        String classPackage = tableEntity.getTableName().split("_")[1];
        context.internalPut("classPackage", classPackage);
        context.internalPut("capClassPackge ", columnToJava(classPackage));
        //获取模板列表
        Map<String, String> templateMap = getTemplates();

        for (Map.Entry<String, String> entry : templateMap.entrySet()) {
            //渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(entry.getValue(), "UTF-8");
            tpl.merge(context, sw);

            try {
                //添加到zip
                zip.putNextEntry(new ZipEntry(getFileName(entry.getValue(), tableEntity.getClassName(), config.getString("package"), config.getString("mainModule"), classPackage)));
                IOUtils.write(sw.toString(), zip, "UTF-8");
                IOUtils.closeQuietly(sw);
                zip.closeEntry();
            } catch (IOException e) {
                throw new RuntimeException("渲染模板失败，表名：" + tableEntity.getTableName(), e);
            }
            entry.getKey();
            entry.getValue();
        }
    }

    /**
     * 封装列的属性信息
     *
     * @param columns
     * @param tableEntity
     * @param config
     * @return
     */
    public static List<ColumnEntity> warpColumnEntityData(List<Map<String, String>> columns, TableEntity tableEntity, final Configuration config) {
        List<ColumnEntity> columsList = new ArrayList<>();
        for (Map<String, String> column : columns) {
            ColumnEntity columnEntity = new ColumnEntity();
            columnEntity.setColumnName(column.get("columnName"));
            if (column.get("dataType").equals("datetime")) {
                columnEntity.setDataType("TIMESTAMP");
            } else if (column.get("dataType").equals("int")) {
                columnEntity.setDataType("INTEGER");
            } else {
                columnEntity.setDataType(column.get("dataType").toUpperCase());
            }
            columnEntity.setComments(column.get("columnComment"));
            columnEntity.setExtra(column.get("extra"));

            //列名转换成Java属性名
            String attrName = GeneratorUtil.columnToJava(columnEntity.getColumnName());
            columnEntity.setAttrName(attrName);
            columnEntity.setAttrname(StringUtils.uncapitalize(attrName));

            //列的数据类型，转换成Java类型
            String attrType = config.getString(columnEntity.getDataType(), "unknowType");
            columnEntity.setAttrType(attrType);

            //是否主键
            if ("PRI".equalsIgnoreCase(column.get("columnKey")) && tableEntity.getPk() == null) {
                tableEntity.setPk(columnEntity);
            }

            columsList.add(columnEntity);
        }
        return columsList;
    }

    /**
     * 生成base代码
     */
    public static void generatorBaseCode(List<String> templates, ZipOutputStream zip) {
        //设置velocity资源加载器
        Configuration config = GeneratorUtil.getConfig();
        VelocityContext context = GeneratorUtil.setVelocityContextInfo(null, config);
        //获取模板列表
        for (String template : templates) {
            //渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, "UTF-8");
            tpl.merge(context, sw);

            try {
                //添加到zip
                zip.putNextEntry(new ZipEntry(getFileName(template, "", config.getString("package"), config.getString("mainModule"), "")));
                IOUtils.write(sw.toString(), zip, "UTF-8");
                IOUtils.closeQuietly(sw);
                zip.closeEntry();
            } catch (IOException e) {
                throw new RuntimeException("渲染模板失败，表名：", e);
            }
        }
    }


    /**
     * 获取文件名
     */
    public static String getFileName(String template, String className, String packageName, String moduleName, String classPackage) {
        String packagePath = moduleName + "-server" + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
        String vuePath = moduleName + "-ui" + File.separator + "vue" + File.separator + "src" + File.separator;
        String xmlPath = moduleName + "-server" + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "mapper" + File.separator;
        String vueClassname = StringUtil.splitCaseByUpper(className);

        if (StringUtils.isNotBlank(packageName)) {
            packagePath += packageName.replace(".", File.separator) + File.separator;
        }

        if (template.contains("basemapper.java.vm")) {
            return packagePath + "mapper" + File.separator + "BaseDao.java";
        }

        if (template.contains("mapper.java.vm")) {
            return packagePath + "mapper" + File.separator + classPackage + File.separator + className + "Mapper.java";
        }

        if (template.contains("po.java.vm")) {
            return packagePath + "model" + File.separator + "po" + File.separator + classPackage + File.separator + className + "PO.java";
        }

        if (template.contains("query.param.java.vm")) {
            return packagePath + "model" + File.separator + "param" + File.separator + classPackage + File.separator + className + "QueryParam.java";
        }

        if (template.contains("curd.param.java.vm")) {
            return packagePath + "model" + File.separator + "param" + File.separator + classPackage + File.separator + className + "OpParam.java";
        }

        if (template.contains("controller.java.vm")) {
            return packagePath + "controller" + File.separator + classPackage + File.separator + className + "Controller.java";
        }

        if (template.contains("mapper.xml.vm")) {
            return xmlPath + classPackage + File.separator + className + "Mapper.xml";
        }

        if (template.contains("service.java.vm")) {
            return packagePath + "service" + File.separator + classPackage + File.separator + className + "Service.java";
        }

        if (template.contains("serviceImpl.java.vm")) {
            return packagePath + "service" + File.separator + classPackage + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
        }

        if (template.contains("template/vue/router/router.js.vm")) {
            return vuePath + "router" + File.separator + classPackage + File.separator + vueClassname + "-module.js";
        }

        if (template.contains("template/vue/service/api.js.vm")) {
            return vuePath + "service" + File.separator + classPackage + File.separator + vueClassname + "-api.js";
        }

        if (template.contains("template/vue/view/create.vue.vm")) {
            return vuePath + "view" + File.separator + classPackage + File.separator + StringUtils.uncapitalize(className) + "Create.vue";
        }

        if (template.contains("template/vue/view/detail.vue.vm")) {
            return vuePath + "view" + File.separator + classPackage + File.separator + StringUtils.uncapitalize(className) + "Detail.vue";
        }

        if (template.contains("template/vue/view/page.vue.vm")) {
            return vuePath + "view" + File.separator + classPackage + File.separator + StringUtils.uncapitalize(className) + "Page.vue";
        }

        if (template.contains("template/vue/view/update.vue.vm")) {
            return vuePath + "view" + File.separator + classPackage + File.separator + StringUtils.uncapitalize(className) + "Update.vue";
        }

        if (template.contains("template/vue/view/history.vue.vm")) {
            return vuePath + "view" + File.separator + classPackage + File.separator + StringUtils.uncapitalize(className) + "History.vue";
        }

        if (template.contains("template/vue/enums/enums.js.vm")) {
            return vuePath + "enums" + File.separator + classPackage + File.separator + vueClassname + "-enums.js";
        }

        return null;
    }


    public static TableEntity getTableEntity(Map<String, String> table,
                                             List<Map<String, String>> columns, Configuration config) {
        //获取表数据新
        TableEntity tableEntity = GeneratorUtil.getTableEntity(table, config, columns);
        //列信息
        List<ColumnEntity> columsList = warpColumnEntityData(columns, tableEntity, config);
        tableEntity.setColumns(columsList);

        //没主键，则第一个字段为主键
        if (tableEntity.getPk() == null) {
            tableEntity.setPk(tableEntity.getColumns().get(0));
        }

        // 设置类包名
        String classPackage = tableEntity.getTableName().split("_")[1];
        tableEntity.setClassPackage(classPackage);

        return tableEntity;
    }

    public static VelocityContext getVelocityContext(TableEntity tableEntity, Configuration config) {
        //设置velocity资源加载器
        VelocityContext context = GeneratorUtil.setVelocityContextInfo(tableEntity, config);
        context.internalPut("classPackage", tableEntity.getClassPackage());
        context.internalPut("capClassPackge ", columnToJava(tableEntity.getClassPackage()));
        context.internalPut("emptyStr", "");
        return context;
    }

    /**
     * 生成代码
     */
    public static TableEntity generatorLocalCode(Map<String, String> table,
                                                 List<Map<String, String>> columns) {
        //配置信息
        Configuration config = GeneratorUtil.getConfig();
        //获取表数据新
        TableEntity tableEntity = getTableEntity(table, columns, config);
        VelocityContext context = getVelocityContext(tableEntity, config);
        //获取模板列表
        Map<String, String> templateMap = getTemplates();
        for (Map.Entry<String, String> entry : templateMap.entrySet()) {
            generateTemplate(entry.getValue(), context, tableEntity, config);
        }
        return tableEntity;
    }

    /**
     * 根据模板生成代码
     *
     * @param template
     * @param context
     * @param tableEntity
     * @param config
     */
    public static void generateTemplate(String template, VelocityContext context, TableEntity tableEntity, Configuration config) {
        //渲染模板
        if (template == null) {
            return;
        }
        StringWriter sw = new StringWriter();
        Template tpl = Velocity.getTemplate(template, "UTF-8");
        tpl.merge(context, sw);

        try {
            //添加到
            String codePath = config.getString("localPath") + getFileName(template, tableEntity.getClassName(), config.getString("package"), config.getString("mainModule"), tableEntity.getClassPackage());
            File file = new File(codePath.substring(0, codePath.lastIndexOf(File.separator)));
            if (!file.exists()) {
                file.mkdirs();
            }

            FileOutputStream fileOutputStream = new FileOutputStream(codePath);
            org.apache.commons.io.IOUtils.write(sw.toString(), fileOutputStream, "UTF-8");
            org.apache.commons.io.IOUtils.closeQuietly(sw);
            fileOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException("渲染模板失败，表名：" + tableEntity.getTableName(), e);
        }
    }

    /**
     * 生产vue 路由文件
     *
     * @param tables
     */
    public static void generatorVueRouter(List<Map<String, String>> tables) {
        //配置信息
        Configuration config = GeneratorUtil.getConfig();
        String localPath = config.getString("localPath");
        //设置velocity资源加载器
        //
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);
        Map<String, Object> map = new HashMap<>(1);
        map.put("tables", tables);

        VelocityContext context = new VelocityContext(map);
        //渲染模板
        StringWriter sw = new StringWriter();
        String routerTemplate = "template/vue/router/index.js.vm";
        Template tpl = Velocity.getTemplate(routerTemplate, "UTF-8");
        tpl.merge(context, sw);

        try {
            String vuePath = config.getString("mainModule") + "-ui" + File.separator + "vue" + File.separator + "src" + File.separator;
            String codePath = localPath + vuePath + "router" + File.separator + "index.js";

            File file = new File(codePath.substring(0, codePath.lastIndexOf(File.separator)));
            if (!file.exists()) {
                file.mkdirs();
            }

            FileOutputStream fileOutputStream = new FileOutputStream(codePath);
            org.apache.commons.io.IOUtils.write(sw.toString(), fileOutputStream, "UTF-8");
            org.apache.commons.io.IOUtils.closeQuietly(sw);
            fileOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException("渲染模板失败", e);
        }
    }
}


